﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using nwrole.lib;

namespace nwrole.Azure {
    public class AzureServiceProvider : nwrole.lib.IServiceProvider {

        public event NwroleSettingsChangedHandler SupervisorSettingsChangedEvent;
        public event NwroleSettingsChangedHandler ServiceSettingsChangedEvent;

        public void Start() {

        }

        public string[] GetServiceNames() {
            List<string> serviceNames = new List<string>();

            string appDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);

            string servicesDir = Path.Combine(appDir, "services");
            if (!Directory.Exists(servicesDir))
                Directory.CreateDirectory(servicesDir);

            CloudBlobContainer blobContainer = GetBlobContainer("services");

            foreach (IListBlobItem blobItem in blobContainer.ListBlobs()) {
                CloudBlob blob = blobItem as CloudBlob;
                if (blob != null && blob.Uri.ToString().EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) {
                    string fileName = Path.GetFileName(blob.Uri.ToString());
                    string serviceName = fileName.Remove(fileName.Length - 4);

                    string localFilePath = Path.Combine(servicesDir, fileName);

                    blob.FetchAttributes();
                    DateTime dateStamp = blob.Properties.LastModifiedUtc;
                    //DateTime dateStamp = DateTime.Parse(blob.Metadata["Date"]);

                    bool download = true;

                    if (File.Exists(localFilePath)) {
                        download = false;
                        DateTime fileDate = File.GetCreationTimeUtc(localFilePath);
                        if (fileDate > dateStamp) {
                            File.Delete(localFilePath);
                            download = true;
                        }
                    }

                    if (download) {
                        string servicePath = Path.Combine(servicesDir, serviceName);
                        if (!Directory.Exists(servicePath))
                            Directory.CreateDirectory(servicePath);

                        blob.DownloadToFile(localFilePath);
                        File.SetCreationTimeUtc(localFilePath, dateStamp);

                        FileStream filestream = File.OpenRead(localFilePath);
                        UnpackZip(filestream, servicePath);
                    }

                    serviceNames.Add(serviceName);
                }
            }

            return serviceNames.ToArray();
        }

        public lib.PropertyList LoadSupervisorSettings() {
            CloudBlobContainer blobContainer = GetBlobContainer("services");

            PropertyList plist = null;

            var blob = blobContainer.GetBlobReference("supervisor-config.xml");
            try {
                using (BlobStream blobStream = blob.OpenRead()) {
                    plist = new PropertyList(blobStream);
                    blobStream.Close();
                }
            } catch {
                return null;
            }

            return plist;
        }

        public void SaveSupervisorSettings(lib.PropertyList plist) {
            CloudBlobContainer blobContainer = GetBlobContainer("services");

            var blob = blobContainer.GetBlobReference("supervisor-config.xml");

            BlobStream blobStream = blob.OpenWrite();
            plist.Save(blobStream);
            blobStream.Close();
        }

        public lib.PropertyList LoadServiceSettings(string serviceName) {
            CloudBlobContainer blobContainer = GetBlobContainer("services");

            string filename = serviceName + ".zip";

            var blob = blobContainer.GetBlobReference(filename);
            blob.FetchAttributes();

            string settings = blob.Metadata["ServiceSettings"];
            if (settings == null)
                return null;

            PropertyList plist = PropertyList.LoadData(System.Web.HttpUtility.HtmlDecode(settings));

            return plist;
        }

        public void SaveServiceSettings(lib.PropertyList plist, string serviceName) {
            CloudBlobContainer blobContainer = GetBlobContainer("services");

            string filename = serviceName + ".zip";

            var blob = blobContainer.GetBlobReference(filename);
            blob.FetchAttributes();

            string text = plist.ToXml();

            blob.Metadata["ServiceSettings"] = System.Web.HttpUtility.HtmlEncode(text);

            blob.SetMetadata();
        }

        private CloudBlobClient GetBlobClient() {
            //var storageAccount = CloudStorageAccount.Parse("DefaultEndpointsProtocol=https;AccountName=<YOURACCOUNTNAME>;AccountKey=<YOURACCOUNTKEY>");
            var storageAccount = CloudStorageAccount.FromConfigurationSetting("AzureStorage.ConnectionString");
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            // For large file copies you need to set up a custom timeout period
            // and using parallel settings appears to spread the copy across multiple threads
            // if you have big bandwidth you can increase the thread number below
            // because Azure accepts blobs broken into blocks in any order of arrival.
            blobClient.Timeout = new System.TimeSpan(1, 0, 0);
            blobClient.ParallelOperationThreadCount = 2;

            return blobClient;
        }

        private CloudBlobContainer GetBlobContainer(string name) {

            CloudBlobClient blobClient = GetBlobClient();

            // Get and create the container
            CloudBlobContainer blobContainer = blobClient.GetContainerReference(name);
            blobContainer.CreateIfNotExist();

            // Setup the permissions on the container to be private
            var permissions = new BlobContainerPermissions();
            permissions.PublicAccess = BlobContainerPublicAccessType.Off;
            blobContainer.SetPermissions(permissions);

            return blobContainer;
        }

        private void UnpackZip(Stream stream, string destPath) {

            //string StorageDirectoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            //Directory.CreateDirectory(StorageDirectoryPath);
            Directory.CreateDirectory(destPath);

            using (ZipInputStream zipStream = new ZipInputStream(stream)) {
                ZipEntry entry;
                while ((entry = zipStream.GetNextEntry()) != null) {
                    /*if (!String.IsNullOrEmpty(entry.Name) && entry.Name.Equals(@"solution.plist", StringComparison.OrdinalIgnoreCase)) {
                        MemoryStream dataStream = new MemoryStream();
                        byte[] buffer = new byte[8192];
                        int size = zipStream.Read(buffer, 0, buffer.Length);
                        while (size > 0) {
                            dataStream.Write(buffer, 0, size);
                            size = zipStream.Read(buffer, 0, buffer.Length);
                        };
                        dataStream.Seek(0, SeekOrigin.Begin);
                        PropertyList plist = new PropertyList(dataStream);
                        Id = new Guid(plist.StringForKey("id"));
                        Name = plist.StringForKey("name");
                        Description = plist.StringForKey("description");
                        Author = plist.StringForKey("author");
                        Created = plist.DateTimeForKey("created");
                        Updated = plist.DateTimeForKey("updated");
                        foreach (object dict in plist.ArrayForKey("services"))
                            Services.Add(new Service((IDictionary)dict));
                    } else*/
                    {
                        using (FileStream fileStream = File.Create(Path.Combine(destPath, entry.Name))) {
                            byte[] buffer = new byte[8192];
                            int size;
                            while ((size = zipStream.Read(buffer, 0, buffer.Length)) > 0)
                                fileStream.Write(buffer, 0, size);
                            fileStream.Close();
                        }
                        //Files.Add(Path.Combine(StorageDirectoryPath, entry.Name));
                    }
                }
            }
        }


        public Dictionary<string, object> GetPluginProviderSettings() {
          var ret = new Dictionary<string, object>();
          ret.Add("AzureStorageConnectionString", RoleEnvironment.GetConfigurationSettingValue("AzureStorage.ConnectionString"));
          return ret;
        }

        public void PulseService(Guid id, string serviceName, string message)
        {
            
        }

        public void PulsePlugin(Guid id, string pluginName, string message)
        {
            
        }

        public ServicePulse GetServicePulse(Guid serviceId)
        {
            
            return null;
        }

        public PluginPulse GetPluginPulse(Guid pluginId)
        {
            
            return null;
        }
    }
}
